﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Threading;
using ActionLogic;
using BehaviorModel.Block;
using BehaviorModel.Block.Finish;
using BehaviorModel.Block.Generator;
using BehaviorModel.Block.ServiceChannel;
using BehaviorModel.Block.Splitter;
using BehaviorModel.Block.Summator;

namespace BehaviorModel.Statistic
{
    public class SystemStatistic : ISystemStatistic
    {
        private ISet<Guid> _busyChannels = new SortedSet<Guid>();

        private double _lastUpdatedTime;

        public object Locker { get; private set; }

        public long CountOfGenerated { get; private set; }

        public long CountOfPassed
        {
            get { return StatisticController.GetInstance().Collector.CountOfPassed; }
        }

        public long CountInSystem
        {
            get { return CountOfGenerated - CountOfPassed; }
        }

        public long CountOfWorkingSC { get; private set; }


        public long CountOfRejected
        {
            get { return StatisticController.GetInstance().Collector.CountOfRejected; }
        }

        public long CountOfServiced
        {
            get { return StatisticController.GetInstance().Collector.CountOfServiced; }
        }

        public double RejectProbability
        {
            get
            {
                if ((CountOfRejected + CountOfServiced) != 0)
                {
                    return ((double)CountOfRejected) / (CountOfRejected + CountOfServiced);
                }
                return 0;
            }
        }


        public double AvgCommonServiceTime
        {
            get { return StatisticController.GetInstance().Collector.AvgCommonServicedTime; }
        }

        public double AvgCommonWaitingTime
        {
            get { return StatisticController.GetInstance().Collector.AvgCommonWaitingTime; }
        }

        public double AvgCommonLiveTime
        {
            get { return StatisticController.GetInstance().Collector.AvgCommonLiveTime; }
        }


        public double ServAvgCommonServiceTime
        {
            get { return StatisticController.GetInstance().Collector.ServAvgCommonServicedTime; }
        }

        public double ServAvgCommonWaitingTime
        {
            get { return StatisticController.GetInstance().Collector.ServAvgCommonWaitingTime; }
        }

        public double ServAvgCommonLiveTime
        {
            get { return StatisticController.GetInstance().Collector.ServAvgCommonLiveTime; }
        }

        public double RejAvgCommonServiceTime
        {
            get { return StatisticController.GetInstance().Collector.RejAvgCommonServicedTime; }
        }

        public double RejAvgCommonWaitingTime
        {
            get { return StatisticController.GetInstance().Collector.RejAvgCommonWaitingTime; }
        }

        public double RejAvgCommonLiveTime
        {
            get { return StatisticController.GetInstance().Collector.RejAvgCommonLiveTime; }
        }

        public Dictionary<Guid, double> AvgQueueLengthByChannels { get; private set; }

        public Dictionary<int, double> ServiceChannelsWorkingDistribution { get; private set; }

        public Dictionary<int, double> RejectProbabilityHistogramData { get; private set; }

        #region IExecutable

        public Action Execute
        {
            get { return Update; }
        }

        public double Priority
        {
            get { return 0; }
        }

        public bool IsBackground
        {
            get { return true; }
        }

        public event Action<PriorityAction> OnActionExecuted;

        #endregion

        public SystemStatistic()
        {
            Locker = new object();

            ServiceChannelsWorkingDistribution = Enumerable.Range(0, BlockController.GetInstance().GetServiceChannels().Count + 1)
                .ToDictionary(item => item, item => 0d);

            RejectProbabilityHistogramData = Enumerable.Range(0, 20).ToDictionary(item => item, item => 0d);
        }

        public void Update()
        {
            BlockController.GetInstance().UpdateBlocks();

            UpdateCountOfGenerated();
            UpdateCountOfWorkingSC();
            UpdateAverageQueueLengthByChannels();
            InitRejectProbabilityHistogramData();
        }

        private void InitRejectProbabilityHistogramData()
        {
            if (RejectProbability != 0d && RejectProbabilityHistogramData.ContainsValue(0d))
            {
                UpdateRejectProbabilityHistogramData();
            } 
        }

        private void UpdateAverageQueueLengthByChannels()
        {
            AvgQueueLengthByChannels = BlockController.GetInstance().GetServiceChannels().GroupBy(b => b.Id).
                ToDictionary(g => g.Key, g => (g.First().Statistic as IServiceChannelStatistic).AverageQueueLength);
        }

        private void UpdateCountOfGenerated()
        {
            CountOfGenerated = BlockController.GetInstance().GetGenerators().Select(g => g.Statistic).Cast<IGeneratorStatistic>()
                .Sum(stat => stat.CountOfGenerated);
        }

        private void UpdateCountOfWorkingSC()
        {
            CountOfWorkingSC = BlockController.GetInstance().GetServiceChannels().Select(g => g.Statistic).Cast<IServiceChannelStatistic>()
                .Count(stat => stat.IsBusy);
        }

        public void UpdateServiceChannelsWorkingDistribution(Guid serviceChannelId, double time, bool isBusy)
        {
            lock (Locker)
            {
                if (isBusy)
                {
                    _busyChannels.Add(serviceChannelId);
                }
                else
                {
                    _busyChannels.Remove(serviceChannelId);
                }

                ServiceChannelsWorkingDistribution[_busyChannels.Count] += (time - _lastUpdatedTime) > 0 ? time - _lastUpdatedTime : 0;
                _lastUpdatedTime = time;
            }
        }

        public void UpdateRejectProbabilityHistogramData()
        {
            int i;
            for (i = 1; i < RejectProbabilityHistogramData.Count; i++)
            {
                RejectProbabilityHistogramData[i - 1] = RejectProbabilityHistogramData[i];
            }
            RejectProbabilityHistogramData[i - 1] = RejectProbability;
        }


    }
}